/*
 * Copyright 2005-2007 Russ Cox, Massachusetts Institute of Technology
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#ifndef TASK_H
#define TASK_H

#include <stdarg.h>
#include <inttypes.h>

#ifdef __cplusplus
extern "C" {
#endif

/*
 * basic procs and threads
 */

typedef struct Task Task;
typedef struct Tasklist Tasklist;

int anyready(void);
int taskcreate(void (*f)(void *arg), void *arg, unsigned int stacksize);
void taskexit(int);
void taskexitall(int);
void taskmain(int argc, char *argv[]);
int taskyield(void);
void **taskdata(void);
void needstack(int);
void taskname(char*, ...);
void taskstate(char*, ...);
char *taskgetname(void);
char *taskgetstate(void);
void tasksystem(void);
unsigned int taskdelay(unsigned int);
unsigned int taskid(void);

/* used internally */
struct Tasklist {
	Task *head;
	Task *tail;
};

/*
 * queuing locks
 */
typedef struct QLock QLock;
struct QLock {
	Task *owner;
	Tasklist waiting;
};

void qlock(QLock*);
int canqlock(QLock*);
void qunlock(QLock*);

/*
 * reader-writer locks
 */
typedef struct RWLock RWLock;
struct RWLock {
	int readers;
	Task *writer;
	Tasklist rwaiting;
	Tasklist wwaiting;
};

void rlock(RWLock*);
int canrlock(RWLock*);
void runlock(RWLock*);

void wlock(RWLock*);
int canwlock(RWLock*);
void wunlock(RWLock*);

/*
 * sleep and wakeup (condition variables)
 */
typedef struct Rendez Rendez;

struct Rendez {
	QLock *l;
	Tasklist waiting;
};

void tasksleep(Rendez*);
int taskwakeup(Rendez*);
int taskwakeupall(Rendez*);

/*
 * channel communication
 */
typedef struct Alt Alt;
typedef struct Altarray Altarray;
typedef struct Channel Channel;

enum {
	CHANEND,
	CHANSND,
	CHANRCV,
	CHANNOP,
	CHANNOBLK
};

struct Alt {
	Channel *c;
	void *v;
	unsigned int op;
	Task *task;
	Alt *xalt;
};

struct Altarray {
	Alt **a;
	unsigned int n;
	unsigned int m;
};

struct Channel {
	unsigned int bufsize;
	unsigned int elemsize;
	unsigned char *buf;
	unsigned int nbuf;
	unsigned int off;
	Altarray asend;
	Altarray arecv;
	char *name;
};

int chanalt(Alt *alts);
Channel *chancreate(int elemsize, int elemcnt);
void chanfree(Channel *c);
int chaninit(Channel *c, int elemsize, int elemcnt);
int channbrecv(Channel *c, void *v);
void *channbrecvp(Channel *c);
unsigned long channbrecvul(Channel *c);
int channbsend(Channel *c, void *v);
int channbsendp(Channel *c, void *v);
int channbsendul(Channel *c, unsigned long v);
int chanrecv(Channel *c, void *v);
void *chanrecvp(Channel *c);
unsigned long chanrecvul(Channel *c);
int chansend(Channel *c, void *v);
int chansendp(Channel *c, void *v);
int chansendul(Channel *c, unsigned long v);

/*
 * Threaded I/O.
 */
int fdread(int, void*, int);
int fdread1(int, void*, int); /* always uses fdwait */
int fdwrite(int, void*, int);
void fdwait(int, int);
int fdnoblock(int);

void fdtask(void*);

/*
 * Network dialing - sets non-blocking automatically
 */
enum {
	UDP = 0,
	TCP = 1
};

int netannounce(int, char*, int);
int netaccept(int, char*, int*);
int netdial(int, char*, int);
int netlookup(char*, uint32_t*); /* blocks entire program! */
int netdial(int, char*, int);

#ifdef __cplusplus
}
#endif
#endif /* TASK_H */
